一、前言

直接复制代码就可以用,实现了格式为tar.gz的压缩格式,也可以实现8GB以上的大文件的压缩与解压,主要是在解压的逻辑中添加了:

1
2
3
4
// 可压缩大于8GB的文件
tOut.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
// 可压缩长名称文件
tOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);

具体可参考:

二、pom依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.20</version>
</dependency>

三、代码实现

压缩时支持超过8GB的大文件、长名称压缩,压缩完成后,会在压缩文件中,创建一个子目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* http://www.imooc.com/article/309519
* 压缩并删除原目录
*
* @param dataPath 要压缩的目录路径
* @param dirName 压缩包里面的目录名称
* @throws IOException ioexception
*/
public static void compressTarGzip(String dataPath, String dirName) throws IOException {
// 被压缩打包的文件夹
Path source = Paths.get(dataPath);
//如果不是文件夹抛出异常
if (!Files.isDirectory(source)) {
throw new IOException("请指定一个文件夹");
}

//压缩之后的输出文件名称
String tarFileName = dataPath + ".tar.gz";
logger.info("正在执行文件压缩: [{}]", dataPath);
//OutputStream输出流、BufferedOutputStream缓冲输出流
//GzipCompressorOutputStream是gzip压缩输出流
//TarArchiveOutputStream打tar包输出流(包含gzip压缩输出流)
try (OutputStream fOut = Files.newOutputStream(Paths.get(tarFileName));
BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {
//遍历文件目录树
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {

//当成功访问到一个文件
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) throws IOException {

// 判断当前遍历文件是不是符号链接(快捷方式),不做打包压缩处理
if (attributes.isSymbolicLink()) {
return FileVisitResult.CONTINUE;
}

// 获取当前遍历文件名称
Path targetFile = source.relativize(file);
// 附带要压缩的目录名
targetFile = Paths.get(dirName, targetFile.toString());

//将该文件打包压缩
TarArchiveEntry tarEntry = new TarArchiveEntry(
file.toFile(), targetFile.toString());
// 可压缩大于8GB的文件
tOut.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
// 可压缩长名称文件
tOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
tOut.putArchiveEntry(tarEntry);
Files.copy(file, tOut);
tOut.closeArchiveEntry();
//继续下一个遍历文件处理
return FileVisitResult.CONTINUE;
}

//当前遍历文件访问失败
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
logger.error("无法对该文件压缩打包为tar.gz, {}", file, exc);
return FileVisitResult.CONTINUE;
}
});
//for循环完成之后,finish-tar包输出流
tOut.finish();
FileUtils.forceDelete(new File(dataPath));
logger.info("相关备份文件已压缩: [{}]", tarFileName);
}
}

/**
* 解压tar.gz压缩包
*
* @param file 解压文件路径,示例:D:\BaiduNetdiskDownload\xxx
* @param path 解压到的目录,示例:D:\BaiduNetdiskDownload
* @throws IOException ioexception
*/
public static void unCompressTarGzip(String file, String path) throws IOException {
//解压文件
Path source = Paths.get(file + ".tar.gz");
//解压到哪
Path target = Paths.get(path);

if (Files.notExists(source)) {
throw new IOException("解压文件不存在");
}
logger.info("正在执行文件解压: [{}]", file + ".tar.gz");
//InputStream输入流,以下四个流将tar.gz读取到内存并操作
//BufferedInputStream缓冲输入流
//GzipCompressorInputStream解压输入流
//TarArchiveInputStream解tar包输入流
try (InputStream fi = Files.newInputStream(source);
BufferedInputStream bi = new BufferedInputStream(fi);
GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

ArchiveEntry entry;
while ((entry = ti.getNextEntry()) != null) {

//获取解压文件目录,并判断文件是否损坏
Path newPath = zipSlipProtect(entry, target);

if (entry.isDirectory()) {
//创建解压文件目录
Files.createDirectories(newPath);
} else {
//再次校验解压文件目录是否存在
Path parent = newPath.getParent();
if (parent != null) {
if (Files.notExists(parent)) {
Files.createDirectories(parent);
}
}
// 将解压文件输入到TarArchiveInputStream,输出到磁盘newPath目录
Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
logger.info("相关文件已解压完成,路径: [{}]", path);
}

四、参考博客

http://www.imooc.com/article/309519 ,里面有详细的解释,也有对单文件的压缩示例。